package com.lollipop.shiro.config;

import com.lollipop.shiro.CredentialMatcher;
import com.lollipop.shiro.realm.AuthRealm;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

/**
 * @Author: J.K
 * @Date: 2021-06-03 08:54
 * @Description: shiro配置类
 */
@Configuration
public class ShiroConfig {
    /**
     * 使用默认的用户认证
     *
     * @return
     */
    @Bean
    public AuthRealm authRealm() {
        AuthRealm authRealm = new AuthRealm();
        // 设置缓存
        authRealm.setCacheManager(new MemoryConstrainedCacheManager());
        return authRealm;
    }

    /**
     * 使用自定义凭证匹配器的AuthRealm
     *
     * @param matcher
     * @return
     */
//    @Bean("authRealm")
//    public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher) {
//        AuthRealm authRealm = new AuthRealm();
//        // 设置缓存（默认，也可以配合redis等缓存组件使用）
//        authRealm.setCacheManager(new MemoryConstrainedCacheManager());
//        authRealm.setCredentialsMatcher(matcher);
//        return authRealm;
//    }

    /**
     * SecurityManager是Shiro的核心
     *
     * @param authRealm
     * @return
     */
    @Bean
    public SecurityManager securityManager(AuthRealm authRealm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(authRealm);
        return defaultWebSecurityManager;
    }

    /**
     * SecurityManager构建完成后加入Shiro过滤器工厂
     *
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 将SecurityManager放入Shiro过滤器工厂
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 设置登录路径、登录成功跳转路径、未授权跳转路径
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        // 设置过滤规则
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/index", "authc");
        filterChainDefinitionMap.put("login", "anon");
        filterChainDefinitionMap.put("/loginUser", "anon");
        filterChainDefinitionMap.put("/admin", "roles[admin]");
        filterChainDefinitionMap.put("/edit", "perms[edit]");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/**", "user");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 自定义用户名密码验证（使用默认的验证也行，对于需要MD5等密码加密的需要使用自定义的验证）-本Demo使用默认的，实际项目中需要使用自定义校验器
     *
     * @return
     */
    @Bean
    public CredentialMatcher credentialMatcher() {
        return new CredentialMatcher();
    }


    /**
     * 以上配置已经实现了基于URL的粗粒度权限控制
     * 想要细粒度权限控制，做到方法级别的权限控制需要添加以下配置（注解式权限的2个配置）
     * e.g:@RequiresPermissions(value = {"user:list", "user:info"}, logical = Logical.OR)
     */

    /**
     * AuthorizationAttributeSourceAdvisor相当于一个切点
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * DefaultAdvisorAutoProxyCreator相当于一个切面
     * AdvisorAutoProxyCreator代理生成器，需要借助AOP来扫描@RequiresRoles和@RequiresPermissions等注解，生成代理类实现功能增强，从而实现权限控制
     * 需要配合AuthorizationAttributeSourceAavisor一起使用，否则权限注解无效
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }


}
